package handler

import (
	"github.com/gin-gonic/gin"
	"github.com/gin-gonic/gin/binding"
	"log"
	"net/http"
	"time"
)

type LoginForm struct {
	User     string `form:"user" binding:"required"`
	Password string `form:"password" binding:"required"`
}

type Person struct {
	Name     string    `form:"name" json:"name"`
	Address  string    `form:"address" json:"address"`
	Birthday time.Time `json:"birthday" form:"birthday" time_format:"2006-01-02"`
}

type formA struct {
	Foo string `json:"foo" xml:"foo" binding:"required"`
}
type formB struct {
	Bar string `json:"bar" xml:"bar" binding:"required"`
}

type Login struct {
	User     string `form:"user" json:"user" xml:"user" binding:"required"`
	Password string `form:"password" json:"password" xml:"password" binding:"required"`
}

func Longin(c *gin.Context) {
	var form LoginForm

	//显式绑定声明 绑定 multipart form
	//c.ShouldBindWith(&form, binding.Form)
	// 或者简单使用ShouldBind 方法自动绑定
	err := c.ShouldBind(&form)

	if err == nil {
		if form.User == "user" && form.Password == "password" {
			c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
		} else {
			c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})
		}
	} else {
		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
	}
}

func Form(c *gin.Context) {
	message := c.PostForm("message")
	nick := c.DefaultPostForm("nick", "anonymous")
	c.JSON(http.StatusOK, gin.H{
		"status":  "posted",
		"message": message,
		"nick":    nick,
	})
}

func QueryAndForm(c *gin.Context) {

	id := c.Query("id")
	page := c.DefaultQuery("page", "0")
	name := c.PostForm("name")
	message := c.PostForm("message")

	c.JSON(http.StatusOK, gin.H{
		"id":      id,
		"page":    page,
		"name":    name,
		"message": message,
	})
}

func StartPage(c *gin.Context) {
	var person Person
	// 如果是 `GET` 请求，只使用 `Form` 绑定引擎（`query`）。
	// 如果是 `POST` 请求，首先检查 `content-type` 是否为 `JSON` 或 `XML`，然后再使用 `Form`（`form-data`）。
	// 查看更多：https://github.com/gin-gonic/gin/blob/master/binding/binding.go#L88
	err := c.ShouldBind(&person)
	if err == nil {
		//if c.ShouldBindQuery(&person) == nil {
		log.Println(person.Name)
		log.Println(person.Address)
		log.Println(person.Birthday)
		c.JSON(http.StatusOK, "Success")
	} else {
		c.JSON(http.StatusOK, gin.H{"error": err.Error()})
	}

}

func BindBodyOnce(c *gin.Context) {
	objA := formA{}
	objB := formB{}

	if errA := c.ShouldBind(&objA); errA == nil {
		//c.String(http.StatusOK, `the body should be formA`)
		log.Println("the body should be formA")
		log.Println("objA:", objA)
	}

	if errB := c.ShouldBind(&objB); errB == nil {
		//c.String(http.StatusOK, `the body should be formB`)
		log.Println("the body should be formB")
		log.Println("objB:", objB)
	}

}

func BindBodyMulti(c *gin.Context) {
	objA := formA{}
	objB := formB{}
	objC := formB{}
	if errA := c.ShouldBindBodyWith(&objA, binding.JSON); errA == nil {
		//c.String(http.StatusOK, `the body should be formA`)
		log.Println("the body should be formA")
		log.Println("objA:", objA)
	}

	if errB := c.ShouldBindBodyWith(&objB, binding.JSON); errB == nil {
		//c.String(http.StatusOK, `the body should be formB`)
		log.Println("the body should be formB")
		log.Println("objB:", objB)
	}

	if errC := c.ShouldBindBodyWith(&objC, binding.XML); errC == nil {
		//c.String(http.StatusOK, `the body should be formB`)
		log.Println("the body should be formB")
		log.Println("objC:", objC)
	}
}

func BindMap(c *gin.Context) {
	ids := c.QueryMap("ids")
	names := c.PostFormMap("names")

	for k, v := range ids {
		names[k] = v
	}

	c.JSON(http.StatusOK, names)
}

func BindQueryParameter(c *gin.Context) {
	firstname := c.DefaultQuery("firstname", "Guest")
	lastname := c.Query("lastname") //c.Request.URL.Query().Get("lastname") 的一种快捷方式

	c.String(http.StatusOK, "Hello %s %s", firstname, lastname)
}

//
//curl --location --request POST 'http://127.0.0.1:9080/bind/loginXml' \
//--header 'User-Agent: apifox/1.0.0 (https://www.apifox.cn)' \
//--header 'Content-Type: application/xml' \
//--data-raw '<root>
//    <user>manu</user>
//    <password>123</password>
//</root>'
//
//
//

func LoginJson(c *gin.Context) {
	var json Login
	if err := c.ShouldBindJSON(&json); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": err.Error(),
		})
		return
	}

	if json.User != "manu" || json.Password != "123" {
		c.JSON(http.StatusUnauthorized, gin.H{
			"status": "unauthorized",
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
}

func LoginXML(c *gin.Context) {
	var xml Login
	if err := c.ShouldBindXML(&xml); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": err.Error(),
		})
		return
	}

	if xml.User != "manu" || xml.Password != "123" {
		c.JSON(http.StatusUnauthorized, gin.H{
			"status": "unauthorized",
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
}

func LoginUseForm(c *gin.Context) {
	var form Login
	if err := c.ShouldBind(&form); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"error": err.Error(),
		})
		return
	}

	if form.User != "manu" || form.Password != "123" {
		c.JSON(http.StatusUnauthorized, gin.H{
			"status": "unauthorized",
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})
}

type myForm struct {
	Colors []string `form:"colors[]"`
}

func BindCheckBox(c *gin.Context) {
	var fakeForm myForm
	c.ShouldBind(&fakeForm)

	c.JSON(http.StatusOK, gin.H{
		"color": fakeForm.Colors,
	})
}

type Person2 struct {
	ID   string `uri:"id" binding:"required,uuid"`
	Name string `uri:"name" binding:"required"`
}

//http://127.0.0.1:9080/bind/uri/zhangsan/987fbc97-4bed-5078-9f07-9141ba07c9f3
func BindUri(c *gin.Context) {
	var person Person2
	if err := c.ShouldBindUri(&person); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"msg": err.Error(),
		})
		return
	}
	c.JSON(http.StatusOK, gin.H{
		"name": person.Name,
		"uuid": person.ID,
	})
}

type StructA struct {
	FieldA string `form:"field_a"`
}

type StructB struct {
	NestedStruct StructA
	FieldB       string `form:"field_b"`
}

type StructC struct {
	NestedStructPointer *StructA
	FieldC              string `form:"field_c"`
}

type StructD struct {
	NestedAnonyStruct struct {
		FieldX string `form:"field_x"`
	}
	FieldD string `form:"field_d"`
}

func GetDataB(c *gin.Context) {
	var b StructB
	c.Bind(&b)
	c.JSON(200, gin.H{
		"a": b.NestedStruct,
		"b": b.FieldB,
	})
}
func GetDataC(c *gin.Context) {
	var b StructC
	c.Bind(&b)
	c.JSON(200, gin.H{
		"a": b.NestedStructPointer,
		"c": b.FieldC,
	})
}
func GetDataD(c *gin.Context) {
	var b StructD
	c.Bind(&b)
	c.JSON(200, gin.H{
		"x": b.NestedAnonyStruct,
		"d": b.FieldD,
	})
}
